//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_OS_BASE_DATE_H
#define _UXS_OS_BASE_DATE_H

#pragma once

#include "Common.h"

namespace UX_STL
{
namespace _platform_
{
	

	//
	// Date
	//

	struct Date
	{
	// types
	public:
		struct EWeekDay
		{
			enum type
			{
				MON = 0,
				TUE,
				WED,
				THU,
				FRI,
				SAT,
				SUN,
				_COUNT
			};
		};

		struct EMonth
		{
			enum type
			{
				JAN = 0,
				FEB,
				MAR,
				APR,
				MAY,
				JUN,
				JUL,
				AUG,
				SEP,
				OCT,
				NOV,
				DEC,
				_COUNT
			};
		};


	// variables
	public:
		uint16	year,
				month,
				dayOfWeek,
				dayOfYear,
				day,
				hour,
				minute,
				second,
				milliseconds;


	// methods
	public:
		Date():
			year(0), month(0), dayOfWeek(0), day(0), dayOfYear(0),
			hour(0), minute(0), second(0), milliseconds(0)
		{}


		EWeekDay::type	GetDayOfWeek ()		const	{ return EWeekDay::type( dayOfWeek ); }
		EMonth::type	GetMonth ()			const	{ return EMonth::type( month-1 ); }

		bool			IsLeapYear ()		const	{ return IsLeapYear( year ); }
		static bool		IsLeapYear (uint year)		{ return ((year & 0x3) == 0 and year % 100 != 0); }

		uint			TimeInMillisec ()	const	{ return ((hour * 60 + minute) * 60 + second) * 1000 + milliseconds; }


		// Implement for current platform
		Date & Now ();


		void Repair()
		{
			//TODO( "fix it" );

			year		  += (month-1)		/ 12;
			
			const int	month_days	 = _GetMonthDays( IsLeapYear() )[ month-1 ];

			month		  += (day-1)		/ month_days;
			day			  += hour			/ 24;
			minute		  += second			/ 60;
			second		  += milliseconds	/ 1000;

			month		   = (month-1) % 12 + 1;
			day			  %= month_days;
			hour		  %= 24;
			minute		  %= 60;
			second		  %= 60;
			milliseconds  %= 1000;

			_CalcDayOfWeek();
			_CalcDayOfYear();
		}


		bool operator == (const Date &r) const
		{
			return	year == r.year and dayOfYear == r.dayOfYear and TimeInMillisec() > r.TimeInMillisec();
		}


		bool operator > (const Date &r) const
		{
			return	  year != r.year ? year > r.year :
					( dayOfYear != r.dayOfYear ? dayOfYear > r.dayOfYear :
					  TimeInMillisec() > r.TimeInMillisec() );
		}


		bool operator < (const Date &r) const
		{
			return	  year != r.year ? year < r.year :
					( dayOfYear != r.dayOfYear ? dayOfYear < r.dayOfYear :
					  TimeInMillisec() < r.TimeInMillisec() );
		}


	private:
		void _CalcDayOfYear ()
		{
			const uni_array<const int>	year_monthes = _GetMonthDays( IsLeapYear() );

			dayOfYear = 0;

			for (int i = 0; i < (int)month-1; ++i) {
				dayOfYear += year_monthes[i];
			}
			dayOfYear += day;
		}


		void _CalcDayOfWeek ()
		{
			int	m = month - 2;
			int y = year;

			if ( m <= 0 )
			{
				m += 12;
				y--;
			}

			int n = (day + (13 * m - 1) / 5 + (y % 100) + (y / 100) / 4 + (y % 100) / 4 - 2 * (y / 100)) % 7;

			if ( n < 0 )
				n += 7;

			dayOfWeek = n == 0 ? 6 : n-1;
		}


		static const uni_array<const int>  _GetMonthDays ()
		{
			static const int	s_aMonthDays[24] = {
				31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
				31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31		// leap year
			};
			return s_aMonthDays;
		}


		static const uni_array<const int>  _GetMonthDays (bool leapYear)
		{
			return uni_array<const int>( _GetMonthDays().ptr() + (int(leapYear)+1) * EMonth::_COUNT, EMonth::_COUNT );
		}
	};

	
//-------------------------------------------------------------------

}	// _platform_
}	// UX_STL

#endif	// _UXS_OS_BASE_DATE_H